//
//  EJCameraShotVC.m
//  TeachingAssistantDemo
//
//  Created by Lius on 2017/7/14.
//  Copyright © 2017年 Lius. All rights reserved.
//

#import "EJCameraShotVC.h"
#import <CoreMotion/CoreMotion.h>
#import <EJTools/EJTools.h>
#import "UIView+EJFrame.h"
#import <Masonry/Masonry.h>
#import <FDFullscreenPopGesture/UINavigationController+FDFullscreenPopGesture.h>
#import "EJPhotoSelectDefine.h"
#import "EJPhotoBrowser.h"
#import "EJImageCropperVC.h"

#import "UIViewController+LSAuthorization.h"
#import "NSString+EJShot.h"
#import "UIImage+EJClicpShotImage.h"

#import "EJAssetModifyModel.h"

@interface EJCameraShotVC ()<EJCameraShotDelegate, AVCaptureFileOutputRecordingDelegate, EJPhotoBrowserDelegate, EJImageCropperDelegate> {
    NSUInteger _shotCount;
}

@property (nonatomic, weak) id <EJCameraShotVCDelegate> delegate;

@property (nonatomic, assign) NSTimeInterval shotTime;

@property (nonatomic, strong) EJCameraShotView * shotView;

@property (strong,nonatomic) AVCaptureSession *captureSession;//负责输入和输出设置之间的数据传递
@property (strong,nonatomic) AVCaptureDeviceInput *captureDeviceInput;//负责从AVCaptureDevice获得输入数据
@property (strong,nonatomic) AVCaptureMovieFileOutput *captureMovieFileOutput;//视频输出流
@property (nonatomic, strong) AVCaptureConnection * captureCon;

//照片输出流
@property (nonatomic, strong) AVCaptureStillImageOutput *stillImageOutput;

@property (strong, nonatomic) AVCaptureVideoPreviewLayer *captureVideoPreviewLayer;//相机拍摄预览图层

@property (nonatomic, copy) NSString *uuid;

@property (nonatomic, copy) NSString *fileName;

@property (nonatomic, copy) NSString *localFilePath;

@property (nonatomic, strong) NSURL *outPutURL;

@property (nonatomic, strong) NSMutableArray * assetIds;

@property (nonatomic, strong) NSMutableSet <EJAssetModifyModel *>* modifySet;


@property (nonatomic, strong) EJProgressHUD *hud;

@property (nonatomic, strong) CMMotionManager* manager;
@property (nonatomic, strong) NSOperationQueue *queque;

@property (nonatomic, assign) E_VideoOrientation suggestOrientation;
@property (nonatomic, assign) AVCaptureVideoOrientation orientation;

@property (nonatomic, strong) NSMutableArray * browserSource;

@property (nonatomic, strong) UILabel * orientationLabel;

@property (nonatomic, weak) NSTimer * freeSizeTimer;

@property (nonatomic, strong) PHImageRequestOptions * options;

@end

@implementation EJCameraShotVC

#pragma mark - life cycle
- (instancetype)initWithShotTime:(NSTimeInterval)shotTime delegate:(id<EJCameraShotVCDelegate>)delegate suggestOrientation:(E_VideoOrientation)suggestOrientation /*allowPreview:(BOOL)allowPreview*/ maxCount:(NSUInteger)maxCount {
    self = [super init];
    if (self) {
        _delegate = delegate;
        _shotTime = shotTime;
        _shotType = EJ_ShotType_Both;
        _maxCount = maxCount;
        _suggestOrientation = suggestOrientation;
        _shotCount = 0;
        
        _forcedCrop = NO;
        _cropScale = 0;
        
        self.allowBoth = YES;
    }
    return self;
}

- (instancetype)initWithShotTime:(NSTimeInterval)shotTime shotType:(EJ_ShotType)shotType delegate:(id<EJCameraShotVCDelegate>)delegate suggestOrientation:(E_VideoOrientation)suggestOrientation /*allowPreview:(BOOL)allowPreview*/ maxCount:(NSUInteger)maxCount {
    self = [super init];
    if (self) {
        _delegate = delegate;
        _shotTime = shotTime;
        _shotType = shotType;
        _maxCount = maxCount;
        _suggestOrientation = suggestOrientation;
        _shotCount = 0;
        
        _forcedCrop = NO;
        _cropScale = 0;
        _directCrop = NO;
        
        self.allowBoth = YES;
    }
    return self;
}

- (void)dealloc {
    [self removeObserver:self forKeyPath:@"orientation"];
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (void)viewDidLoad {
    [super viewDidLoad];
    self.fd_prefersNavigationBarHidden = YES;
    // Do any additional setup after loading the view.
    self.view.backgroundColor = [UIColor blackColor];
    [self initCamera];
    _shotView = [[EJCameraShotView alloc] initWithFrame:self.view.bounds shotTime:_shotTime shotType:self.shotType];
    _shotView.delegate = self;
    _shotView.allowBoth = _allowBoth;
    [self.view addSubview:_shotView];
    
    [_shotView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.edges.equalTo(self.view);
    }];
    
    [self.view addSubview:self.orientationLabel];
    if (_suggestOrientation == E_VideoOrientationAll) {
        _orientationLabel.hidden = YES;
    } else {
        if (_suggestOrientation == AVCaptureVideoOrientationPortraitUpsideDown || _suggestOrientation == AVCaptureVideoOrientationPortrait) {
            _orientationLabel.text = @"建议竖屏拍摄";
        } else {
            _orientationLabel.text = @"建议横屏拍摄";
        }
    }
    
    //kvo
    [self addObserver:self forKeyPath:@"orientation" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
    
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleWillResignActive) name:UIApplicationWillResignActiveNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleBecomeActive) name:UIApplicationDidBecomeActiveNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleCaptureSessionRuntimeError:) name:AVCaptureSessionRuntimeErrorNotification object:nil];

    _queque = [[NSOperationQueue alloc] init];
}

- (void)startMinitorOrientation {
    if (_manager == nil) {
        _manager = [[CMMotionManager alloc] init];
    }
    _manager.deviceMotionUpdateInterval = 1;
    if (_manager.deviceMotionAvailable == YES) {
        __weak typeof(self) weak_self = self;
        [_manager startDeviceMotionUpdatesToQueue:_queque withHandler:^(CMDeviceMotion * _Nullable motion, NSError * _Nullable error) {
            [weak_self handleDeviceMotion:motion];
        }];
    } else {
        _manager = nil;
    }
}

- (void)handleDeviceMotion:(CMDeviceMotion *)deviceMotion {
    double x = deviceMotion.gravity.x;
    double y = deviceMotion.gravity.y;
    if (fabs(y) >= fabs(x)) {
        if (y >= 0) {
            self.orientation = AVCaptureVideoOrientationPortraitUpsideDown;
        } else {
            self.orientation = AVCaptureVideoOrientationPortrait;
        }
    } else {
        if (x >= 0){
            self.orientation =  AVCaptureVideoOrientationLandscapeRight;
        } else {
            self.orientation =  AVCaptureVideoOrientationLandscapeLeft;
        }
    }
}

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    if (_captureSession.isRunning == NO) {
        [_captureSession startRunning];
    }
    @weakify(self);
    [self judgeAppPhotoLibraryUsageAuth:^(PHAuthorizationStatus status) {
        if (status == PHAuthorizationStatusAuthorized) {
            @strongify(self);
            [self startMinitor];
        }
    }];
}

- (void)startMinitor {
    @weakify(self);
    dispatch_async(dispatch_get_main_queue(), ^{
        [weak_self startMinitorOrientation];
    });
}

- (void)viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];
    if (_captureSession.isRunning) {
        [_captureSession stopRunning];
    }
    if (self.manager.isDeviceMotionActive) {
        [self.manager stopAccelerometerUpdates];
        _manager = nil;
    }
    if ([[NSFileManager defaultManager] fileExistsAtPath:_localFilePath]) {
        [[NSFileManager defaultManager] removeItemAtPath:_localFilePath error:nil];
    }
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (void)initCamera {
    _captureSession = [[AVCaptureSession alloc] init];
    if ([_captureSession canSetSessionPreset:AVCaptureSessionPreset1920x1080]) {//设置分辨率
        _captureSession.sessionPreset = AVCaptureSessionPreset1920x1080;
    } else {
        _captureSession.sessionPreset = AVCaptureSessionPresetHigh;
    }

    //获得输入设备
    AVCaptureDevice *captureDevice = [self getCameraDeviceWithPosition:AVCaptureDevicePositionBack];//取得后置摄像头
    if (!captureDevice) {
        NSLog(@"取得后置摄像头时出现问题.");
        return;
    }


    //添加一个音频输入设备
    AVCaptureDevice *audioCaptureDevice = [[AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio] firstObject];


    NSError *error=nil;
    //根据输入设备初始化设备输入对象，用于获得输入数据
    _captureDeviceInput = [[AVCaptureDeviceInput alloc] initWithDevice:captureDevice error:&error];
    if (error) {
        NSLog(@"取得设备输入对象时出错，错误原因：%@",error.localizedDescription);
        return;
    }
    
    if (_shotType == EJ_ShotType_Photo) {
        // 将设备输入添加到会话中
        if ([_captureSession canAddInput:_captureDeviceInput]) {
            [_captureSession addInput:_captureDeviceInput];
        }
        
        //将设备输出添加到会话中
        self.stillImageOutput = [[AVCaptureStillImageOutput alloc] init];
        NSDictionary *outputSettings = @{
            AVVideoCodecKey: AVVideoCodecJPEG,
        };
        [self.stillImageOutput setOutputSettings:outputSettings];
        if ([self.captureSession canAddOutput:self.stillImageOutput]) {
            [self.captureSession addOutput:self.stillImageOutput];
        }
    } else if (_shotType == EJ_ShotType_Video) {
        AVCaptureDeviceInput *audioCaptureDeviceInput = [[AVCaptureDeviceInput alloc] initWithDevice:audioCaptureDevice error:&error];
        if (error) {
            NSLog(@"取得设备输入对象时出错，错误原因：%@",error.localizedDescription);
            return;
        }
        // 初始化设备输出对象，用于获得输出数据
        _captureMovieFileOutput = [[AVCaptureMovieFileOutput alloc] init];
        // 将设备输入添加到会话中
        if ([_captureSession canAddInput:_captureDeviceInput]) {
            [_captureSession addInput:_captureDeviceInput];
            [_captureSession addInput:audioCaptureDeviceInput];
            AVCaptureConnection *captureConnection = [_captureMovieFileOutput connectionWithMediaType:AVMediaTypeVideo];
            [captureConnection setEnabled:NO];
            if ([captureConnection isVideoStabilizationSupported]) {
                captureConnection.preferredVideoStabilizationMode = AVCaptureVideoStabilizationModeAuto;
            }
        }
        
        if ([_captureSession canAddOutput:_captureMovieFileOutput]) {
            [_captureSession addOutput:_captureMovieFileOutput];
        }
    } else {
        AVCaptureDeviceInput *audioCaptureDeviceInput = [[AVCaptureDeviceInput alloc] initWithDevice:audioCaptureDevice error:&error];
        if (error) {
            NSLog(@"取得设备输入对象时出错，错误原因：%@",error.localizedDescription);
            return;
        }
        // 初始化设备输出对象，用于获得输出数据
        _captureMovieFileOutput = [[AVCaptureMovieFileOutput alloc] init];
        // 将设备输入添加到会话中
        if ([_captureSession canAddInput:_captureDeviceInput]) {
            [_captureSession addInput:_captureDeviceInput];
            [_captureSession addInput:audioCaptureDeviceInput];
            AVCaptureConnection *captureConnection = [_captureMovieFileOutput connectionWithMediaType:AVMediaTypeVideo];
            [captureConnection setEnabled:NO];
            if ([captureConnection isVideoStabilizationSupported]) {
                captureConnection.preferredVideoStabilizationMode = AVCaptureVideoStabilizationModeAuto;
            }
        }
        
        // 将设备输出添加到会话中
        self.stillImageOutput = [[AVCaptureStillImageOutput alloc] init];
        NSDictionary *outputSettings = @{
            AVVideoCodecKey: AVVideoCodecJPEG,
        };
        [self.stillImageOutput setOutputSettings:outputSettings];
        if ([self.captureSession canAddOutput:self.stillImageOutput]) {
            [self.captureSession addOutput:self.stillImageOutput];
        }
        
        if ([_captureSession canAddOutput:_captureMovieFileOutput]) {
            [_captureSession addOutput:_captureMovieFileOutput];
        }
    }

    //创建视频预览层，用于实时展示摄像头状态
    _captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.captureSession];
    CALayer *layer = self.view.layer;
    layer.masksToBounds = YES;
        
    CGFloat height = kScreenWidth / 3.f * 4.f;
    CGFloat bottomSpace = 96 + kBottomSafeHeight;
    CGFloat top = kScreenHeight - bottomSpace - height;
    if (top < 0) {
        top = 0;
    }
    CGRect frame = CGRectMake(0, top, kScreenWidth, height);
    if (top > (kScreenHeight - CGRectGetMaxY(frame))) {
        frame.origin.y = (kScreenHeight - CGRectGetHeight(frame)) / 2.f;
    }
    _captureVideoPreviewLayer.frame = frame;
    
    _captureVideoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;//填充模式
    //将视频预览层添加到界面中
    [layer addSublayer:_captureVideoPreviewLayer];
}

- (void)setupCameraPreset:(AVCaptureSessionPreset)preset isImage:(BOOL)isImage {
    if ([_captureSession canSetSessionPreset:preset]) {//设置分辨率
        _captureSession.sessionPreset = preset;
    } else {
        _captureSession.sessionPreset = AVCaptureSessionPresetHigh;
    }
    CALayer *layer = self.view.layer;
    layer.masksToBounds = YES;
    if (isImage) {

        CGFloat height = kScreenWidth / 3.f * 4.f;
        CGFloat bottomSpace = 96 + kBottomSafeHeight;
        CGFloat top = kScreenHeight - bottomSpace - height;
        if (top < 0) {
            top = 0;
        }
        _captureVideoPreviewLayer.frame = CGRectMake(0, top, kScreenWidth, height);
    } else {
        _captureVideoPreviewLayer.frame = layer.bounds;
    }
}

- (void)handleChangeDevice {
    NSArray *inputs = self.captureSession.inputs;
    for (AVCaptureDeviceInput *input in inputs) {
        AVCaptureDevice *device = input.device;
        if ([device hasMediaType:AVMediaTypeVideo]) {
            AVCaptureDevicePosition position = device.position;
            AVCaptureDevice *newCamera = nil;
            AVCaptureDeviceInput *newInput = nil;

            if (position == AVCaptureDevicePositionFront) {
                newCamera = [self getCameraDeviceWithPosition:AVCaptureDevicePositionBack];
                if ([_captureSession canSetSessionPreset:AVCaptureSessionPreset1920x1080]) {//设置分辨率
                    _captureSession.sessionPreset = AVCaptureSessionPreset1920x1080;
                } else {
                    _captureSession.sessionPreset = AVCaptureSessionPresetHigh;
                }
            } else {
                newCamera = [self getCameraDeviceWithPosition:AVCaptureDevicePositionFront];
                _captureSession.sessionPreset = AVCaptureSessionPresetHigh;
            }
            newInput = [AVCaptureDeviceInput deviceInputWithDevice:newCamera error:nil];

            // beginConfiguration ensures that pending changes are not applied immediately
            [self.captureSession beginConfiguration];

            [self.captureSession removeInput:input];
            
            if ([_captureSession canAddInput:newInput]) {
                [self.captureSession addInput:newInput];
            }

            // Changes take effect once the outermost commitConfiguration is invoked.
            [self.captureSession commitConfiguration];
            break;
        }
    }
}

- (void)handleTakePhoto {

    AVCaptureConnection *videoConnection = [self.stillImageOutput connectionWithMediaType:AVMediaTypeVideo];

    if (!videoConnection) {
        _shotCount -= 1;
        return;
    }

    _shotView.userInteractionEnabled = NO;
    @weakify(self);
    [self.stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
        @strongify(self);
        [self saveImageWithBuffer:imageDataSampleBuffer error:error];
    }];
}

- (void)saveImageWithBuffer:(CMSampleBufferRef)imageDataSampleBuffer error:(NSError *)error {
    
    if (imageDataSampleBuffer == NULL) {
        _shotCount -= 1;
        @weakify(self);
        dispatch_async(dispatch_get_main_queue(), ^{
            weak_self.shotView.userInteractionEnabled = YES;
        });
        return;
    }
    NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
    if (imageData == nil) {
        _shotCount -= 1;
        @weakify(self);
        dispatch_async(dispatch_get_main_queue(), ^{
            weak_self.shotView.userInteractionEnabled = YES;
        });
        return;
    }
    UIImage *image = [self flipImage:[UIImage imageWithData:imageData]];
    if (image.size.height > image.size.width) {
        image = [image clipImage:CGSizeMake(kScreenWidth * [UIScreen mainScreen].scale, kScreenWidth / 3 * 4 * [UIScreen mainScreen].scale)];
    } else {
        image = [image clipImage:CGSizeMake(kScreenWidth / 3 * 4 * [UIScreen mainScreen].scale, kScreenWidth * [UIScreen mainScreen].scale)];
    }
    UIImage *resultImg = [image fixOrientation];
    @weakify(self);
    [[EJSaveToAlbum mainSave] saveImage:resultImg successBlock:^(NSString *assetLocalId) {
        @strongify(self);
        [self saveImageFinished:assetLocalId resultImage:resultImg];
        
    } failureBlock:^(NSError *error) {
        @strongify(self);
        [self saveToLocalFailed:error];
    }];
}

- (void)saveImageFinished:(NSString *)assetLocalId resultImage:(UIImage *)resultImg {
    @weakify(self);
    dispatch_async(dispatch_get_main_queue(), ^{
        weak_self.shotView.userInteractionEnabled = YES;
    });
    if ([assetLocalId length] > 0) {
        [self.assetIds addObject:assetLocalId];
        dispatch_async(dispatch_get_main_queue(), ^{
            @strongify(self);
            if (self.maxCount == 1) {
                if (self.directCrop == YES) {
                    [self handleJumpToCropper];
                } else {
                    [self ej_cameraShotViewDidClickPreviews];
                }
            } else {
                self.shotView.img = resultImg;
                self.shotView.previewCount = self.assetIds.count;
            }
        });
    } else {
        _shotCount -= 1;
        dispatch_async(dispatch_get_main_queue(), ^{
            [EJProgressHUD showAlert:@"保存到系统相册失败" forView:weak_self.view];
        });
    }
}

- (void)saveVideoToLocal:(NSString *)assetLocalId coverImage:(UIImage *)coverImage {
    if ([assetLocalId length] > 0) {
        [self.assetIds addObject:assetLocalId];
        @weakify(self);
        dispatch_async(dispatch_get_main_queue(), ^{
            @strongify(self);
            [self.hud hideAnimated:YES];
            if ((self.allowBoth == NO && self.videoShotCount == 1) || (self.allowBoth && self.maxCount == 1)) {
                [self ej_cameraShotViewDidClickDone];
            } else {
                self.shotView.img = coverImage;
                self.shotView.previewCount = self.assetIds.count;
                if ([[NSFileManager defaultManager] fileExistsAtPath:self.localFilePath]) {
                    NSError * fileError = nil;
                    [[NSFileManager defaultManager] removeItemAtPath:self.localFilePath error:&fileError];
                    if (fileError) {
                        NSLog(@"%@", fileError);
                    }
                }
            }
        });
    } else {
        _shotCount -= 1;
        @weakify(self);
        dispatch_async(dispatch_get_main_queue(), ^{
            [EJProgressHUD showAlert:@"保存到系统相册失败" forView:weak_self.view];
        });
    }
}

- (void)saveToLocalFailed:(NSError *)error {
    _shotCount -= 1;
    @weakify(self);
    dispatch_async(dispatch_get_main_queue(), ^{
        @strongify(self);
        self.shotView.userInteractionEnabled = YES;
        [EJProgressHUD showAlert:error.localizedDescription forView:self.view];
    });
}

- (void)recordAction {

    if ([self.captureMovieFileOutput isRecording]) {
        [self startMinitorOrientation];
        [self.captureMovieFileOutput stopRecording];
        _shotCount -= 1;
        return;
    }
    
    if (self.manager.isDeviceMotionActive) {
        [self.manager stopDeviceMotionUpdates];
        _manager = nil;
    }
    
    _uuid = [[NSUUID UUID] UUIDString];
    _fileName = [NSString stringWithFormat:@"%@.mov",_uuid];
    _localFilePath = [NSString stringWithFormat:@"%@/%@" , [NSString ej_shotDicPath], _fileName];
    //转为视频保存的url
    NSURL *url = [NSURL fileURLWithPath:_localFilePath];
    
    AVCaptureConnection * videoConnection = nil;
    for ( AVCaptureConnection *connection in [self.captureMovieFileOutput connections]) {
        for (AVCaptureInputPort *port in [connection inputPorts] ) {
            if ([[port mediaType] isEqual:AVMediaTypeVideo]) {
                videoConnection = connection;
                break;
            }
        }
    }
    
    if ([videoConnection isVideoOrientationSupported]) {
        if (_orientation == AVCaptureVideoOrientationLandscapeLeft) {
            [videoConnection setVideoOrientation:AVCaptureVideoOrientationLandscapeRight];
        } else if (_orientation == AVCaptureVideoOrientationLandscapeRight) {
            [videoConnection setVideoOrientation:AVCaptureVideoOrientationLandscapeLeft];
        } else {
            [videoConnection setVideoOrientation:_orientation];
        }
    }
    [self.captureMovieFileOutput startRecordingToOutputFileURL:url recordingDelegate:self];
    if (_freeSizeTimer) {
        [_freeSizeTimer invalidate];
        _freeSizeTimer = nil;
    }
    NSTimer * timer = [NSTimer scheduledTimerWithTimeInterval:1.f target:self selector:@selector(handleJudgeSystemFreeSize:) userInfo:nil repeats:YES];
    _freeSizeTimer = timer;
}

- (void)stopRecord {
    if (_freeSizeTimer) {
        [_freeSizeTimer invalidate];
        _freeSizeTimer = nil;
    }
    if ([self.captureMovieFileOutput isRecording]) {
        [self startMinitorOrientation];
        [self.captureMovieFileOutput stopRecording];
        return;
    }
}

#pragma mark - action
- (void)handleBecomeActive {
    if (_captureSession.isRunning == NO) {
        [_captureSession startRunning];
    }
}

- (void)handleWillResignActive {
    if ([self.captureMovieFileOutput isRecording]) {
        [self startMinitorOrientation];
        [self.captureMovieFileOutput stopRecording];
        [self.shotView stopRecordVideo];
    }
    if (_captureSession.isRunning) {
        [_captureSession stopRunning];
    }
}

- (void)handleCaptureSessionRuntimeError:(NSNotification *)notification {
    NSDictionary * userInfo = [notification userInfo];
    NSString * error = userInfo[AVCaptureSessionErrorKey];
    NSLog(@"%@", error);
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    if ([keyPath isEqualToString:@"orientation"]) {
        if (_suggestOrientation != E_VideoOrientationAll) {
            NSNumber * oldValue = change[@"old"];
            NSNumber * currValue = change[@"new"];
            if ([oldValue integerValue] != [currValue integerValue]) {
                [self.shotView configOrientation:_orientation];
                [self showAlertOrientationLabel:_orientation];
            }
        }
    }
}

- (void)showAlertOrientationLabel:(AVCaptureVideoOrientation)orientation {
    @weakify(self);
    dispatch_async(dispatch_get_main_queue(), ^{
        @strongify(self);
        if (!self.orientationLabel.superview) {
            return ;
        }
        NSInteger suggest = (NSInteger)self.suggestOrientation;
        NSInteger current = (NSInteger)orientation;
        if (suggest == current || (suggest <= 2 && current <= 2) || (suggest > 2 && current > 2)) {
            self.orientationLabel.hidden = YES;
            return;
        }
        CGAffineTransform transform = CGAffineTransformIdentity;
        self.orientationLabel.hidden = NO;
        switch (orientation) {
            case AVCaptureVideoOrientationPortrait: {
                self.orientationLabel.layer.position = CGPointZero;
                self.orientationLabel.frame = CGRectMake((kScreenWidth - 190) / 2.f, kToolsStatusHeight, 190, 40);
                self.orientationLabel.transform = CGAffineTransformIdentity;
            }
                break;
            case AVCaptureVideoOrientationLandscapeLeft: {
                self.orientationLabel.layer.position = CGPointMake(kScreenWidth/ 2.f, kScreenHeight / 2.f);
                self.orientationLabel.transform = CGAffineTransformIdentity;
                self.orientationLabel.frame = CGRectMake((kScreenWidth - 190) / 2.f, 0, 190, 40);
                self.orientationLabel.transform = CGAffineTransformRotate(transform, M_PI_2);
                self.orientationLabel.ls_left = kScreenWidth - 40;
                self.orientationLabel.ls_centerY = kScreenHeight / 2.f;
            }
                break;
            case AVCaptureVideoOrientationLandscapeRight: {
                self.orientationLabel.layer.position = CGPointMake(kScreenWidth/ 2.f, kScreenHeight / 2.f);
                self.orientationLabel.transform = CGAffineTransformIdentity;
                self.orientationLabel.frame = CGRectMake((kScreenWidth - 190) / 2.f, 0, 190, 40);
                self.orientationLabel.transform = CGAffineTransformRotate(transform, -M_PI_2);
                self.orientationLabel.ls_left = 0;
                self.orientationLabel.ls_centerY = kScreenHeight / 2.f;
            }
                break;
            case AVCaptureVideoOrientationPortraitUpsideDown: {
                self.orientationLabel.layer.position = CGPointZero;
                self.orientationLabel.frame = CGRectMake((kScreenWidth - 190) / 2.f, kScreenHeight - kBottomSafeHeight - 100 - 40, 190, 40);
                self.orientationLabel.transform = CGAffineTransformRotate(transform, M_PI);
            }
                break;
            default:
                break;
        }
        @weakify(self);
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            @strongify(self);
            self.orientationLabel.hidden = YES;
            [self.orientationLabel removeFromSuperview];
        });
    });
}

- (void)handleJudgeSystemFreeSize:(NSTimer *)timer {
    BOOL isEnough = [UIDevice isEnoughFreeSizePer:0.01];
    if (!isEnough) {
        [self stopRecord];
        UIAlertController * alertC = [UIAlertController alertControllerWithTitle:@"停止拍摄" message:@"手机存储空间不足，请先清理空间后再次尝试！" preferredStyle:UIAlertControllerStyleAlert];
        UIAlertAction * cancelAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleCancel handler:nil];
        [alertC addAction:cancelAction];
        [self presentViewController:alertC animated:YES completion:nil];
    }
}

#pragma mark - AVCaptureFileOutputRecordingDelegate
- (void)captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray *)connections error:(NSError *)error {
    NSLog(@"outputFileURL:%@",outputFileURL);
    NSString *outputURl = [NSString stringWithFormat:@"%@", outputFileURL];
    NSLog(@"error:%@",error);
    if (outputURl.length > 0) {
        //转为视频保存的url
        _outPutURL = [NSURL fileURLWithPath:_localFilePath];
        UIImage * coverImage = [UIImage thumbnailImageForVideo:_outPutURL atTime:0.1];

        _hud = [EJProgressHUD ej_showHUDAddToView:self.view animated:YES];
        @weakify(self);
        [[EJSaveToAlbum mainSave] saveVideoWithUrl:_outPutURL successBlock:^(NSString *assetLocalId) {
            @strongify(self);
            [self saveVideoToLocal:assetLocalId coverImage:coverImage];
        } failureBlock:^(NSError *error) {
            @strongify(self);
            [self saveToLocalFailed:error];
        }];
    } else {
        _shotCount -= 1;
        UIAlertController * alertC = [UIAlertController alertControllerWithTitle:nil message:@"保存失败,请重新录制!" preferredStyle:UIAlertControllerStyleAlert];
        UIAlertAction * cancelAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleCancel handler:nil];
        [alertC addAction:cancelAction];
        [self presentViewController:alertC animated:YES completion:nil];
    }

}

#pragma mark - EJCameraShotDelegate

- (void)ej_cameraShotViewDidClickToClose {
    [self dismissViewControllerAnimated:YES completion:nil];
}

- (void)configShotDone {
    if ([self.delegate respondsToSelector:@selector(ej_shotVCDidShot:)]) {
        NSMutableArray * result = [NSMutableArray arrayWithCapacity:1];
        for (NSString * obj in self.assetIds) {
            EJAssetModifyModel * modify;
            for (EJAssetModifyModel * item in self.modifySet) {
                if ([item.asset.localIdentifier isEqualToString:obj]) {
                    modify = item;
                    break;
                }
            }
            if (modify) {
                [result addObject:modify];
            } else {
                modify = [[EJAssetModifyModel alloc] init];
                modify.asset = [[PHAsset fetchAssetsWithLocalIdentifiers:@[obj] options:nil] lastObject];
                [result addObject:modify];
            }
        }
        [self.delegate ej_shotVCDidShot:[result copy]];
    }
}

- (void)ej_cameraShotViewDidClickDone {
    if ([_localFilePath length] > 0 && [[NSFileManager defaultManager] fileExistsAtPath:_localFilePath]) {
        [[NSFileManager defaultManager] removeItemAtPath:_localFilePath error:nil];
    }
    [self configShotDone];
    [self dismissViewControllerAnimated:YES completion:^{
        [[NSNotificationCenter defaultCenter] postNotificationName:EJCameraShotDismissedNotification object:nil];
    }];
}

- (void)ej_cameraShotViewDidClickToChangeDevice {
    //切换摄像头
    [self handleChangeDevice];
}

- (void)ej_cameraShotViewDidClickCameraButtonToTakePhoto {
    if (_shotCount >= _maxCount) {
        [EJProgressHUD showAlert:[NSString stringWithFormat:@"最多只能拍摄%d张", (int)_maxCount] forView:self.view];
        return;
    }
    //拍照
    _shotCount += 1;
    [self handleTakePhoto];
}

- (void)showMaxCountHud {
    _hud = [[EJProgressHUD alloc] initWithView:self.view];
    [self.view addSubview:_hud];
    if (_shotType == EJ_ShotType_Photo) {
        _hud.label.text = [NSString stringWithFormat:@"最多只能拍摄%d张照片", (int)_maxCount];
    } else if (_shotType == EJ_ShotType_Video) {
        _hud.label.text = [NSString stringWithFormat:@"最多只能拍摄%d个视频", (int)_maxCount];
    } else {
        if (_allowBoth) {
            _hud.label.text = [NSString stringWithFormat:@"最多只能拍摄%d个照片或视频", (int)_maxCount];
        } else {
            if (_shotView.currentType == E_CurrentType_Photo) {
                _hud.label.text = [NSString stringWithFormat:@"最多只能拍摄%d个照片", (int)_maxCount];
            } else {
                _hud.label.text = [NSString stringWithFormat:@"最多只能拍摄%d个视频", (int)_videoShotCount];
            }
        }
    }
    CGAffineTransform transform = CGAffineTransformIdentity;
    switch (_orientation) {
        case AVCaptureVideoOrientationPortrait: {
            _hud.transform = CGAffineTransformIdentity;
        }
            break;
        case AVCaptureVideoOrientationLandscapeLeft: {
            _hud.transform = CGAffineTransformRotate(transform, M_PI_2);
        }
            break;
        case AVCaptureVideoOrientationLandscapeRight: {
            _hud.transform = CGAffineTransformRotate(transform, -M_PI_2);
        }
            break;
        case AVCaptureVideoOrientationPortraitUpsideDown: {
            _hud.transform = CGAffineTransformRotate(transform, M_PI);
        }
            break;
        default:
            break;
    }
    
    _hud.mode = MBProgressHUDModeText;
    [_hud showAnimated:YES];
    [_hud hideAnimated:YES afterDelay:1.5f];
    [_hud removeFromSuperViewOnHide];
}

- (void)ej_cameraShotViewDidClickToRecordVideoWithStart:(BOOL)isStart {
    if (isStart) {
        _shotCount += 1;
        [self recordAction];
    } else {
        [self stopRecord];
    }
}

- (void)ej_cameraShotViewDidClickToSend {
    if ([_localFilePath length] > 0 && [[NSFileManager defaultManager] fileExistsAtPath:_localFilePath]) {
        [[NSFileManager defaultManager] removeItemAtPath:_localFilePath error:nil];
    }
    //发送文件
    [self configShotDone];
    
    [self dismissViewControllerAnimated:YES completion:nil];
}

- (void)ej_cameraShotViewDidClickToRerecord {
    if ([self.assetIds count] > 0) {
        [self.assetIds removeLastObject];
    }
    if ([NSString stringWithFormat:@"%@",_outPutURL].length > 0) {
        [[NSFileManager defaultManager] removeItemAtPath:_outPutURL.absoluteString error:nil];
    }
}

- (BOOL)ej_cameraShotViewCanShot {
    if (_shotType == EJ_ShotType_Both && _allowBoth == NO) {
        // 判断当前拍摄的是照片还是视频
        if (_shotView.currentType == E_CurrentType_Photo) {
            if ([self.assetIds count] < _maxCount || _shotCount < _maxCount) {
                return YES;
            } else {
                [self showMaxCountHud];
                return NO;
            }
        } else {
            if ([self.assetIds count] < _videoShotCount || _shotCount < _videoShotCount) {
                return YES;
            } else {
                [self showMaxCountHud];
                return NO;
            }
        }
    } else {
        if ([self.assetIds count] < _maxCount || _shotCount < _maxCount) {
            return YES;
        } else {
            [self showMaxCountHud];
            return NO;
        }
    }
}

- (void)ej_cameraShotViewDidClickPreviews {
    [self.browserSource removeAllObjects];
    PHFetchResult <PHAsset *>* result = [PHAsset fetchAssetsWithLocalIdentifiers:self.assetIds options:nil];
    [result enumerateObjectsUsingBlock:^(PHAsset * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        [self.browserSource addObject:[EJPhoto photoWithAsset:obj]];
    }];
    
    EJPhotoBrowser * brower = [[EJPhotoBrowser alloc] initWithDelegate:self];
    brower.showCropButton = YES;
    brower.showSelectButton = YES;
    [brower setCurrentPhotoIndex:0];
    brower.isPreview = YES;
    brower.forcedCrop = _forcedCrop;
    [self.navigationController pushViewController:brower animated:YES];
}

- (void)ej_cameraShotViewDidChangeToShotPhoto:(BOOL)isShotPhoto {
    if (isShotPhoto == YES) {
        // 拍照
        [self setupCameraPreset:AVCaptureSessionPreset1920x1080 isImage:YES];
    } else {
        // 视频
        [self setupCameraPreset:AVCaptureSessionPreset1280x720 isImage:NO];
    }
}

#pragma mark - EJPhotoBrowserDelegate
- (NSUInteger)numberOfPhotosInPhotoBrowser:(EJPhotoBrowser *)photoBrowser {
    return [self.browserSource count];
}

- (id<EJPhoto>)photoBrowser:(EJPhotoBrowser *)photoBrowser photoAtIndex:(NSUInteger)index {
    return [self.browserSource objectAtIndex:index];
}

- (BOOL)photoBrowser:(EJPhotoBrowser *)photoBrowser isPhotoSelectedAtIndex:(NSUInteger)index {
    return YES;
}

- (void)photoBrowser:(EJPhotoBrowser *)photoBrowser photoAtIndex:(NSUInteger)index selectedChanged:(BOOL)selected {
    if (selected == NO) {
        [self.browserSource removeObjectAtIndex:index];
        [self.assetIds removeObjectAtIndex:index];
        _shotCount --;
        if ([self.assetIds count] > 0) {
            NSString * Id = [self.assetIds lastObject];
            PHAsset * asset = [PHAsset fetchAssetsWithLocalIdentifiers:@[Id] options:nil].firstObject;
            @weakify(self);
            [[PHImageManager defaultManager] requestImageForAsset:asset targetSize:CGSizeMake(100, 100) contentMode:PHImageContentModeAspectFill options:nil resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
                weak_self.shotView.img = result;
            }];
            _shotView.previewCount = self.assetIds.count;
        } else {
            _shotView.img = nil;
            _shotView.previewCount = 0;
        }
        [photoBrowser reloadData];
    }
}

- (NSUInteger)photoBrowserMaxSelectePhotoCount:(EJPhotoBrowser *)photoBrowser {
    return _maxCount;
}

- (NSUInteger)photoBrowserSelectedPhotoCount:(EJPhotoBrowser *)photoBrowser {
    return [self.assetIds count];
}

- (CGFloat)photoBrowser:(EJPhotoBrowser *)photoBrowser crapScaleAtIndex:(NSUInteger)index {
    return _cropScale;
}

- (void)photoBrowser:(EJPhotoBrowser *)photoBrowser didCropVideoAtIndex:(NSUInteger)index assetId:(NSString *)assetId {
    NSObject * item;
    if ([self.assetIds count] > index) {
        item = [self.assetIds objectAtIndex:index];
    }
    if (item == nil) {
        [photoBrowser reloadData];
        return;
    }
    PHAsset * asset = [[PHAsset fetchAssetsWithLocalIdentifiers:@[assetId] options:nil] firstObject];
    EJPhoto * currPhoto = [EJPhoto photoWithAsset:asset];
    [self.browserSource replaceObjectAtIndex:index withObject:currPhoto];
    [self.assetIds replaceObjectAtIndex:index withObject:assetId];
    if (index == 0) {
        @weakify(self);
        [[PHImageManager defaultManager] requestImageForAsset:asset targetSize:CGSizeMake(100, 100) contentMode:PHImageContentModeAspectFill options:nil resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
            weak_self.shotView.img = result;
        }];
    }
    [photoBrowser reloadData];
}

- (void)photoBrowser:(EJPhotoBrowser *)photoBrowser didCropImageAtIndex:(NSUInteger)index image:(UIImage *)resultImage {
    NSString * item;
    if ([self.assetIds count] > index) {
        item = [self.assetIds objectAtIndex:index];
    }
    if (item == nil) {
        [photoBrowser reloadData];
        return;
    }

    EJPhoto * currPhoto = [EJPhoto photoWithImage:resultImage];
    currPhoto.modifyId = item;
    [self.browserSource replaceObjectAtIndex:index withObject:currPhoto];
    
    EJAssetModifyModel * modify;
    for (EJAssetModifyModel * obj in self.modifySet) {
        if ([obj.asset.localIdentifier isEqualToString:item]) {
            modify = obj;
            break;
        }
    }
    
    if (modify) {
        modify.image = resultImage;
    } else {
        modify = [[EJAssetModifyModel alloc] init];
        modify.asset = [[PHAsset fetchAssetsWithLocalIdentifiers:@[item] options:nil] lastObject];
        modify.image = resultImage;
        [self.modifySet addObject:modify];
    }
    
    if (index == 0) {
        _shotView.img = resultImage;
    }
    [photoBrowser reloadData];
}

- (void)photoBrowserDidFinish:(EJPhotoBrowser *)photoBrowser {
    [self ej_cameraShotViewDidClickDone];
}

- (void)photoBrowserDidCancel:(EJPhotoBrowser *)photoBrowser {
    if (_maxCount == 1) {
        [self ej_cameraShotViewDidClickToClose];
    } else {
        
    }
}

#pragma mark - EJImageCropperDelegate
- (void)ej_imageCropperVCDidCrop:(UIImage *)image isCrop:(BOOL)isCrop {
    if ([self.delegate respondsToSelector:@selector(ej_shotVC:didCropped:)]) {
        EJAssetModifyModel * modify = [[EJAssetModifyModel alloc] init];
        modify.image = image;
        NSString * firstAssetId = [self.assetIds firstObject];
        modify.asset = [[PHAsset fetchAssetsWithLocalIdentifiers:@[firstAssetId] options:nil] lastObject];
        [self.delegate ej_shotVC:self didCropped:modify];
    }
}

- (void)ej_imageCropperVCDidReset:(PHAsset *)asset {
    
}

- (void)ej_imageCropperVCDidCancel {
    _shotCount = 0;
    [self.assetIds removeAllObjects];
}

- (BOOL)ej_imageCropperVCAutoPopAfterCrop {
    return NO;
}

#pragma mark - private
- (AVCaptureDevice *)getCameraDeviceWithPosition:(AVCaptureDevicePosition)position {

    NSArray *cameras= [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
    for (AVCaptureDevice *camera in cameras) {
        if ([camera position]==position) {
            return camera;
        }
    }
    return nil;
}

- (UIImage*)flipImage:(UIImage*)image {
    
    CGAffineTransform transform = CGAffineTransformIdentity;
    
    if (_orientation == AVCaptureVideoOrientationPortrait) {
        transform = CGAffineTransformTranslate(transform, 0 ,image.size.height);
        transform = CGAffineTransformRotate(transform, -M_PI_2);
        CGContextRef ctx =CGBitmapContextCreate(NULL, image.size.width, image.size.height,
                                                CGImageGetBitsPerComponent(image.CGImage), 0,
                                                CGImageGetColorSpace(image.CGImage),
                                                CGImageGetBitmapInfo(image.CGImage));
        CGContextConcatCTM(ctx, transform);
        CGContextDrawImage(ctx,CGRectMake(0,0,image.size.height,image.size.width), image.CGImage);
        CGImageRef cgimg =CGBitmapContextCreateImage(ctx);
        UIImage *img = [UIImage imageWithCGImage:cgimg];
        CGContextRelease(ctx);
        CGImageRelease(cgimg);
        return img;
    }
    else if (_orientation == AVCaptureVideoOrientationPortraitUpsideDown) {
        transform = CGAffineTransformTranslate(transform, image.size.width, 0);
        transform = CGAffineTransformRotate(transform, M_PI_2);
        CGContextRef ctx =CGBitmapContextCreate(NULL, image.size.width, image.size.height,
                                                CGImageGetBitsPerComponent(image.CGImage),0,
                                                CGImageGetColorSpace(image.CGImage),
                                                CGImageGetBitmapInfo(image.CGImage));
        CGContextConcatCTM(ctx, transform);
        CGContextDrawImage(ctx, CGRectMake(0,0,image.size.height,image.size.width), image.CGImage);
        CGImageRef cgimg = CGBitmapContextCreateImage(ctx);
        UIImage *img = [UIImage imageWithCGImage:cgimg];
        CGContextRelease(ctx);
        CGImageRelease(cgimg);
        return img;
    }
    else if (_orientation == AVCaptureVideoOrientationLandscapeLeft) {
        CGContextRef ctx = CGBitmapContextCreate(NULL, image.size.height, image.size.width,
                                                 CGImageGetBitsPerComponent(image.CGImage), 0,
                                                 CGImageGetColorSpace(image.CGImage),
                                                 CGImageGetBitmapInfo(image.CGImage));
        CGContextConcatCTM(ctx, transform);
        CGContextDrawImage(ctx, CGRectMake(0,0,image.size.height,image.size.width), image.CGImage);
        CGImageRef cgimg = CGBitmapContextCreateImage(ctx);
        UIImage *img = [UIImage imageWithCGImage:cgimg];
        CGContextRelease(ctx);
        CGImageRelease(cgimg);
        return img;
    }
    else if (_orientation == AVCaptureVideoOrientationLandscapeRight) {
        transform = CGAffineTransformTranslate(transform, image.size.height, image.size.width);
        transform = CGAffineTransformRotate(transform, M_PI);
        CGContextRef ctx = CGBitmapContextCreate(NULL, image.size.height, image.size.width,
                                                 CGImageGetBitsPerComponent(image.CGImage), 0,
                                                 CGImageGetColorSpace(image.CGImage),
                                                 CGImageGetBitmapInfo(image.CGImage));
        CGContextConcatCTM(ctx, transform);
        CGContextDrawImage(ctx, CGRectMake(0,0,image.size.height,image.size.width), image.CGImage);
        CGImageRef cgimg = CGBitmapContextCreateImage(ctx);
        UIImage *img = [UIImage imageWithCGImage:cgimg];
        CGContextRelease(ctx);
        CGImageRelease(cgimg);
        return img;
    }
    return image;
}

- (void)handleJumpToCropper {
    PHAsset * result = [[PHAsset fetchAssetsWithLocalIdentifiers:self.assetIds options:nil] firstObject];
    if (result.mediaType == PHAssetMediaTypeImage) {
        // 跳转到图片裁剪页面
        @weakify(self);
        [[PHImageManager defaultManager] requestImageDataForAsset:result options:self.options resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) {
            @strongify(self);
            UIImage * image = [UIImage imageWithData:imageData];
            EJImageCropperVC * vc = [[EJImageCropperVC alloc] initWithImage:image];
            vc.cropScale = _cropScale;
            vc.customCropBorder = _customCropBorder;
            vc.customLayerImage = _customLayerImage;
            vc.warningTitle = _cropWarningTitle;
            vc.delegate = self;
            [self.navigationController pushViewController:vc animated:YES];
        }];
    } else {
        // 跳转到视频裁剪页面
        NSLog(@"跳转到视频裁剪页面");
    }
}

#pragma mark - getter or setter
- (void)setAllowBoth:(BOOL)allowBoth {
    _allowBoth = allowBoth;
    if (_shotView) {
        _shotView.allowBoth = allowBoth;
    }
    if (_allowBoth == NO) {
        if (_videoShotCount == -1) {
            _videoShotCount = _maxCount;
        }
    } else {
        _videoShotCount = -1;
    }
}

- (NSMutableArray *)assetIds {
    if (!_assetIds) {
        _assetIds = [NSMutableArray arrayWithCapacity:1];
    }
    return _assetIds;
}

- (NSMutableSet<EJAssetModifyModel *> *)modifySet {
    if (!_modifySet) {
        _modifySet = [NSMutableSet setWithCapacity:1];
    }
    return _modifySet;
}

- (NSMutableArray *)browserSource {
    if (!_browserSource) {
        _browserSource = [NSMutableArray arrayWithCapacity:1];
    }
    return _browserSource;
}

- (UILabel *)orientationLabel {
    if (!_orientationLabel) {
        _orientationLabel = [[UILabel alloc] initWithFrame:CGRectMake((kScreenWidth - 190) / 2.f, kToolsStatusHeight, 190, 40)];
        _orientationLabel.backgroundColor = [[UIColor colorWithRed:(51 / 255.0) green:(51 / 255.0) blue:(51 / 255.0) alpha:1] colorWithAlphaComponent:0.4];
        _orientationLabel.font = [UIFont systemFontOfSize:13];
        _orientationLabel.textColor = [UIColor colorWithRed:(254 / 255.0) green:(254 / 255.0) blue:(254 / 255.0) alpha:1];
        _orientationLabel.textAlignment = NSTextAlignmentCenter;
        _orientationLabel.layer.cornerRadius = 5;
        _orientationLabel.layer.masksToBounds = YES;
        _orientationLabel.hidden = YES;
    }
    return _orientationLabel;
}

- (PHImageRequestOptions *)options {
    if (!_options) {
        _options = [[PHImageRequestOptions alloc] init];
        _options.resizeMode = PHImageRequestOptionsResizeModeFast;
        _options.deliveryMode = PHImageRequestOptionsDeliveryModeOpportunistic;
    }
    return _options;
}

@end
